package com.zhijian.medical.service.impl;

import cn.hutool.core.util.RandomUtil;
import com.zhijian.medical.config.Constants;
import com.zhijian.medical.entity.dto.PersonDto;
import com.zhijian.medical.entity.po.Person;
import com.zhijian.medical.entity.vo.request.LoginVo;
import com.zhijian.medical.entity.vo.request.UserLoginSaveOrUpdateVo;
import com.zhijian.medical.entity.vo.response.AuthVo;
import com.zhijian.medical.entity.vo.response.MenuVo;
import com.zhijian.medical.enums.PersonStatusEnum;
import com.zhijian.medical.enums.ResponseCodeEnum;
import com.zhijian.medical.enums.RoleNewEnum;
import com.zhijian.medical.exception.BusinessException;
import com.zhijian.medical.service.*;
import com.zhijian.medical.util.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;

/**
 * @author HCG
 * @version 1.0
 * @date 2022/1/17
 */
@Service
@Slf4j
public class LoginServiceImpl implements LoginService {

    @Autowired
    private PersonService personService;

    @Autowired
    private CacheService cacheService;

    @Autowired
    private MenuService menuService;

    @Autowired
    private PersonRoleService personRoleService;

    @Autowired
    private PersonGroupService personGroupService;

    @Autowired
    private UserLoginService userLoginService;

    @Override
    public AuthVo login(LoginVo vo) {
        Person person = personService.selectByJobNumberAndPassword(vo.getJobNumber(), null);
        if (null == person) {
            throw new BusinessException(ResponseCodeEnum.FAILURE.code, "登录用户工号或密码不正确");
        }
        //给前台传过来的密码加盐，然后加密
        String password = EncryptUtil.password(vo.getPassword(), person.getSalt());
        if (!password.equals(person.getPassword())) {
            throw new BusinessException(ResponseCodeEnum.FAILURE.code, "登录用户工号或密码不正确");
        }
        if (person.getStatus().intValue() == PersonStatusEnum.LEAVE.getCode()) {
            throw new BusinessException(ResponseCodeEnum.FAILURE.code, "此用户已经离职，无法登陆");
        }
        //角色id数组
        List<Long> roleIds = personRoleService.selectRolesByPersonId(person.getId());
        //部门id数组
        List<Long> groupIds = personGroupService.selectGroupIdsByPersonId(person.getId());
        PersonDto personDto = PersonDto.mergeFromPo(person);
        assert personDto != null;
        personDto.setSort(RandomUtil.randomInt(99999));
        personDto.setGroupIds(groupIds);
        personDto.setRoleIds(roleIds);
        if (roleIds.contains(RoleNewEnum.ZXPXRY.getCodes().get(0))) {
            //是专项培训人员
            personDto.setWeatherZxpx(1);
        } else {
            personDto.setWeatherZxpx(0);
        }
        //搜一下此角色配置的菜单列表
        List<MenuVo> menuVos = menuService.renderTreeListByPersonId(person.getId());
        //生成新的token(账号、密码、生成密钥、过期时间、登陆时间)
        TokenBean tokenBean = new TokenBean(Constants.TOKEN_ACCESS_KEY, Constants.TOKEN_SECRET_KEY,
                "1.0", "login", "POST", null, JsonUtil.toString(personDto));
        String token = TokenUtil.generateToken(tokenBean, 0L);
        cacheService.setex(String.format(Constants.Redis.USER_LOGIN_TOKEN, person.getJobNumber(), personDto.getSort()), Constants.LOGIN_EXPIRE_TIME, token);
        //缓存生成token的用户信息
        cacheService.setex(String.format(Constants.Redis.TOKEN_LOGIN_USER, token), Constants.LOGIN_EXPIRE_TIME, JsonUtil.toString(personDto));

        RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();
        //获取Request
        HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
        CompletableFuture.runAsync(() -> {
            String ipAddress = RequestUtils.getIpAddress(request);
            UserLoginSaveOrUpdateVo saveOrUpdateVo = new UserLoginSaveOrUpdateVo();
            saveOrUpdateVo.setUserId(person.getId());
            saveOrUpdateVo.setIpAddress(ipAddress);
            saveOrUpdateVo.setType((byte) 1);
            userLoginService.save(saveOrUpdateVo);
        });

        return AuthVo.builder()
                .token(token)
                .menuVos(menuVos)
                .user(AuthVo.UserDetailVo.builder()
                        .id(person.getId())
                        .name(person.getName())
                        .phone(person.getPhone())
                        .jobNumber(person.getJobNumber())
                        .roleIds(roleIds)
                        .groupIds(groupIds)
                        .ability(assembleAbility(roleIds))
                        .weatherZxpx(roleIds.contains(RoleNewEnum.ZXPXRY.getCodes().get(0)) ? 1 : 0)
                        .build())
                .build();
    }

    @Override
    public boolean logout(String token) {
        PersonDto tPartnerUser = cacheService.getPersonDtoByToken(token);
        if (tPartnerUser == null) {
            return false;
        }
        //清空token缓存
        cacheService.del(String.format(Constants.Redis.USER_LOGIN_TOKEN, tPartnerUser.getJobNumber(), tPartnerUser.getSort()));
        //清空token对应用户信息
        cacheService.del(String.format(Constants.Redis.TOKEN_LOGIN_USER, token));
        RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();
        //获取Request
        HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
        CompletableFuture.runAsync(() -> {
            String ipAddress = RequestUtils.getIpAddress(request);
            UserLoginSaveOrUpdateVo saveOrUpdateVo = new UserLoginSaveOrUpdateVo();
            saveOrUpdateVo.setUserId(tPartnerUser.getId());
            saveOrUpdateVo.setIpAddress(ipAddress);
            saveOrUpdateVo.setType((byte) 2);
            userLoginService.save(saveOrUpdateVo);
        });
        return true;
    }

    @Override
    public boolean changePassword(Long id, String oldPassword, String newPassword) {
        Person person = personService.selectById(id);
        if (Objects.isNull(person)) {
            throw new BusinessException(ResponseCodeEnum.FAILURE.code, "用户不存在或者已被删除");
        }
        //校验：账号密码正确性校验
        String pwdMd5 = EncryptUtil.password(oldPassword, person.getSalt());
        if (!pwdMd5.equals(person.getPassword())) {
            throw new BusinessException(ResponseCodeEnum.FAILURE.code, "旧密码不正确");
        }
        personService.updateByPrimaryKeySelective(Person.builder()
                .id(id)
                .password(EncryptUtil.password(newPassword, person.getSalt()))
                .updateTime(new Date())
                .build());
        return true;
    }

    /**
     * 组装能力集
     *
     * @param roleIds
     * @return
     */
    private List<String> assembleAbility(List<Long> roleIds) {
        List<String> ability = new LinkedList<>();
        //是否有申请库存能力
        if (!CollectionUtils.isEmpty(CollectionUtils.intersection(roleIds, RoleNewEnum.SQKZ_RY.getCodes()))) {
            ability.add("APPLY_STOCK");
            //收货能力与申请库存能力一致
            ability.add("TAKE_OVER");
        }
        //是否有第一步审批能力
        if (!CollectionUtils.isEmpty(CollectionUtils.intersection(roleIds, RoleNewEnum.FIRST_APPROVE.getCodes()))) {
            ability.add("FIRST_APPROVE");
        }
        //是否有第二步审批能力
        if (!CollectionUtils.isEmpty(CollectionUtils.intersection(roleIds, RoleNewEnum.SECOND_APPROVE.getCodes()))) {
            ability.add("SECOND_APPROVE");
        }
        //是否有发货能力
        if (!CollectionUtils.isEmpty(CollectionUtils.intersection(roleIds, RoleNewEnum.GONG_YING_SHANG.getCodes()))) {
            ability.add("SEND_OUT");
        }
        return ability;
    }


}
